﻿package  
{
	import adobe.utils.CustomActions;
	import com.bit101.components.Component;
	import com.bit101.components.Panel;
	import com.hurlant.crypto.tests.ITestHarness;
	import flash.display.MovieClip;
	import flash.display.TriangleCulling;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	import flash.text.TextField;
	import flash.text.TextFormat;
	import flash.text.TextFieldAutoSize;
	import mx.rpc.http.HTTPService;
	import mx.rpc.events.ResultEvent;
	import com.adobe.serialization.json.JSON;
	import flash.display.Sprite;
	import flash.display.Stage;
	import graph.Graph;
	import graph.Edge;
	import graph.Node;
	/**
	 * ...
	 * @author s
	 */
	public class FisheyeGraphView extends Component
	{
		private var _graph:Graph;
		private var _nodes:Vector.<FisheyeGraphViewNode>;
		private var _highlightedNode:FisheyeGraphViewNode;
		private var _highlightedNodeLabel:TextField;
		private var _inMotionNode:FisheyeGraphViewNode;
		
		private var _motionCt:int = 0;
		private var _clickedNode:Node;		
		
		private static const TEXT_FORMAT:TextFormat = new TextFormat("ＭＳ Ｐゴシック", 12);
		private static const HIGHLIGHTED_NODE_ALPHA:Number = 0.9;
		private static const NORMAL_NODE_ALPHA:Number = 0.5;
		private static const HIGHLIGHTED_NODE_LABEL_COLOR:int = 0xA0A0A0;
		
		public static const MOUSE_EVENT_CLICK_NODE:String = "click_node";
		
		public function FisheyeGraphView(x:int, y:int, width:int, height:int, graph:Graph) 
		{
			super();
			super.x = x;
			super.y = y;
			super.width = width;
			super.height = height;
			_graph = graph;
			_nodes = new Vector.<FisheyeGraphViewNode>;
			_highlightedNode = null;
			_inMotionNode = null;
			
			_highlightedNodeLabel = new TextField();
			addChild(_highlightedNodeLabel);
			_highlightedNodeLabel.scaleX = _highlightedNodeLabel.scaleY = 1.5;
			_highlightedNodeLabel.visible = false;
			_highlightedNodeLabel.autoSize = TextFieldAutoSize.LEFT;
			_highlightedNodeLabel.selectable = false;
			_highlightedNodeLabel.textColor = HIGHLIGHTED_NODE_LABEL_COLOR;
			_highlightedNodeLabel.setTextFormat(TEXT_FORMAT);
		}
		
		public function addNewNode(node:Node):void 
		{
			var url:String;
			var service:HTTPService = new HTTPService();
			var thisSprite:Sprite = this;
			var taskName:String = "search '" + node.label + "' from google";

			service.url = 'http://ajax.googleapis.com/ajax/services/search/images';
			service.request.v = '1.0';
			service.request.q = node.label;
			//service.request.safe = "off";
			//service.request.imgtype = "face"
			//service.resultFormat = 'text';
			service.addEventListener(ResultEvent.RESULT, function(event:ResultEvent):void {
				try {
					var x:Number = Math.random();
					var y:Number = Math.random();
					var json:Object = JSON.decode(event.result as String);
					var url:String = json["responseData"]["results"][0]["tbUrl"];
					var viewNode:FisheyeGraphViewNode = new FisheyeGraphViewNode(x, y, url, node);
					node.userData = viewNode;
					viewNode.addEventListener(MouseEvent.MOUSE_OVER, onMouseOverToNode);
					viewNode.addEventListener(MouseEvent.MOUSE_OUT,  onMouseOutFromNode);
					viewNode.addEventListener(MouseEvent.CLICK, onClickNode);
					thisSprite.addChild(viewNode);
					_nodes.push(viewNode);
				} catch(ignored:Error) {
				}
				InitJobCounter.getInstance().down(taskName);
			}, false, 0);
			InitJobCounter.getInstance().up(taskName);
			service.send();
		}
		
		private function onEnterFrame(e:Event):void
		{
			var i:int;
			
			for (i = 0; i < _nodes.length; i++) {
				var viewNode:FisheyeGraphViewNode = _nodes[i];
				var x:Number = viewNode.logicalLocation.x * super.width;
				var y:Number = viewNode.logicalLocation.y * super.height;
				var dx:Number = x - super.stage.mouseX;
				var dy:Number = y - super.stage.mouseY;
				var d_2:Number = dx * dx + dy * dy;
				var scale:Number;
				scale = Math.exp(-d_2 / 20000.0) * 2 + 1.0;
				viewNode.scaleX = scale * 0.3;
				viewNode.scaleY = scale * 0.3;
				viewNode.x = super.stage.mouseX + dx * scale - viewNode.width / 2;
				viewNode.y = super.stage.mouseY + dy * scale - viewNode.height / 2;
				
				viewNode.motion();
			}

			super.graphics.clear();
			
			// draw edges
			if (_highlightedNode != null) {
				super.graphics.lineStyle(1, 0x808080, 0.5);
				for (var edgeCt:int = 0; _highlightedNode.attachedNode.adjacentEdge(edgeCt) != null; edgeCt++) {
					var edge:Edge =  _highlightedNode.attachedNode.adjacentEdge(edgeCt);
					var src:FisheyeGraphViewNode = edge.srcNode.userData as FisheyeGraphViewNode;
					var dst:FisheyeGraphViewNode = edge.dstNode.userData as FisheyeGraphViewNode;
					if(src != null && dst != null){
						super.graphics.moveTo(src.x + src.width / 2, src.y + src.height / 2);
						super.graphics.lineTo(dst.x + dst.width / 2, dst.y + dst.height / 2);
					}
				}
				_highlightedNodeLabel.x = _highlightedNode.x + (_highlightedNode.width - _highlightedNodeLabel.width) / 2;
				_highlightedNodeLabel.y = _highlightedNode.y + _highlightedNode.height;
			}
			
			// set motion node
			{
				if (_motionCt == 0) {
					_motionCt = Math.random() * 100;
					if (_inMotionNode == null || Math.random() < 0.1) {
						if(_graph.numNodes > 0){
							_inMotionNode = _graph.node(int(_graph.numNodes * Math.random())).userData as FisheyeGraphViewNode;
						}
					}
					else {
						var n:Node = _inMotionNode.attachedNode;
						if(n.numAdjacentNodes > 0){
							_inMotionNode = n.adjacentNode(n.numAdjacentNodes * Math.random()).userData as FisheyeGraphViewNode;
						}
						else {
							_inMotionNode = null;
						}
					}
					
					if (_inMotionNode != null && _inMotionNode != _highlightedNode) {
						setNodeMotion(_inMotionNode);
					}
				}
				else{
					_motionCt--;
				}
			}
		}

		public function setNodeMotion(node:FisheyeGraphViewNode):void 
		{
			var d:Number = Math.random() * 0.10 + 0.01;
			var direction:int = Math.random() * 4;
			var maxCt:int = 20;
			var dstPt:Point;
			
			if (direction == 0) {
				dstPt = new Point(node.logicalLocation.x + d, node.logicalLocation.y)
			}
			else if (direction == 1) {
				dstPt = new Point(node.logicalLocation.x - d, node.logicalLocation.y)
			}
			else if (direction == 2) {
				dstPt = new Point(node.logicalLocation.x, node.logicalLocation.y + d)
			}
			else if (direction == 3) {
				dstPt = new Point(node.logicalLocation.x, node.logicalLocation.y - d)
			}
			
			if (dstPt.x > super.width) {
				dstPt.x = super.width;
			}
			else if (dstPt.x < 0) {
				dstPt.x = 0;
			}
			if (dstPt.y > super.height) {
				dstPt.y = super.height;
			}
			else if (dstPt.y < 0) {
				dstPt.y = 0;
			}
			node.setMotion(dstPt, maxCt);
		}
		
		public function activate():void 
		{
			addEventListener(Event.ENTER_FRAME, onEnterFrame);
			for (var i:int = 0; i < _nodes.length; i++) {
				_nodes[i].alpha = NORMAL_NODE_ALPHA;
				_nodes[i].visible = true;
			}
		}
		
		private function changeHighlighedNode(node:FisheyeGraphViewNode):void 
		{
			if (_highlightedNode != null) {
				_highlightedNode.alpha = NORMAL_NODE_ALPHA;
			}
			if (node != null) {
				super.addChild(node); // change Z order to top
				super.addChild(_highlightedNodeLabel); // change Z order to top
				node.alpha = HIGHLIGHTED_NODE_ALPHA;
				_highlightedNodeLabel.text = node.attachedNode.label;
				_highlightedNodeLabel.x = node.x + (node.width - _highlightedNodeLabel.width) / 2;
				_highlightedNodeLabel.y = node.y + node.height;
				_highlightedNodeLabel.visible = true;
			}
			else {
				_highlightedNodeLabel.visible = false;
			}

			_highlightedNode = node;
		}
	
		private function onMouseOverToNode(e:Event):void 
		{
			var node:FisheyeGraphViewNode = e.target as FisheyeGraphViewNode;
			//trace("on mouse over: " + node.attachedNode.label);
			
			changeHighlighedNode(node);
		}
		
		private function onMouseOutFromNode(e:Event):void 
		{
			var node:FisheyeGraphViewNode = e.target as FisheyeGraphViewNode;
			//trace("on mouse out: " + node.attachedNode.label);
			
			if (_highlightedNode == node) {
				changeHighlighedNode(null);
			}
		}
		
		private function onClickNode(e:Event):void 
		{
			var event:Event = new Event(MOUSE_EVENT_CLICK_NODE);
			_clickedNode = (e.target as FisheyeGraphViewNode).attachedNode;
			dispatchEvent(event);
		}
		
		public function get clickedNode():Node 
		{
			return _clickedNode;
		}
	}


}



import flash.display.Loader;
import flash.events.Event;
import flash.geom.Point;
import flash.net.URLRequest;
import graph.Node;

internal class FisheyeGraphViewNode extends flash.display.Loader
{
	private var _logicalLocation:Point;
	private var _state:int;
	private var _attachedNode:Node;
	private var _motionStartPos:Point;
	private var _motionEndPos:Point;
	private var _motionCt:int;
	private var _motionMaxCt:int;
	private var _imageURL:String;
	
	private static const STATE_IMAGE_LOADING:int = 1;
	private static const STATE_IMAGE_LOADED:int = 2;

	public function FisheyeGraphViewNode(x:Number, y:Number, imageURL:String, node:Node) 
	{
		_logicalLocation = new Point(x, y);
		_state = STATE_IMAGE_LOADING;
		_attachedNode = node;
		_motionCt = -1;
		_imageURL = imageURL;

		InitJobCounter.getInstance().up(imageURL);
		super();
		super.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete);
		super.load(new URLRequest(_imageURL));
	}
	
	public function setMotion(endPos:Point, maxCt:int):void
	{
		_motionStartPos = logicalLocation.clone();
		_motionEndPos = endPos;
		_motionCt = 0;
		_motionMaxCt = maxCt;
	}
	
	public function motion():void 
	{
		if (_motionCt != -1) {
			var d:Number = (1 - Math.cos(Math.PI * _motionCt / _motionMaxCt)) / 2.0;
			_logicalLocation.x = (_motionEndPos.x - _motionStartPos.x) * d + _motionStartPos.x;
			_logicalLocation.y = (_motionEndPos.y - _motionStartPos.y) * d + _motionStartPos.y;

			_motionCt++;
			if (_motionCt == _motionMaxCt) {
				_motionCt = -1;
			}
		}
	}
	
	private function onComplete(e:Event):void 
	{
		_state = STATE_IMAGE_LOADED;			
		InitJobCounter.getInstance().down(_imageURL);
	}
	
	public function get logicalLocation():Point
	{
		return _logicalLocation;
	}
	
	public function get attachedNode():Node 
	{
		return _attachedNode;
	}
	
	public function get isLoaded():Boolean
	{
		return _state == STATE_IMAGE_LOADED;
	}
}

